package com.huawang.mdesign.model.service.inter.simulationintegration;

import com.huawang.business.command.CommandAddType;
import com.huawang.business.constants.ModelConstantURI;
import com.huawang.business.dto.AddModelDTO;
import com.huawang.business.dto.BatchCommand;
import com.huawang.business.dto.SlotValueInfo;
import com.huawang.business.dto.UpdateSlotValueDto;
import com.huawang.business.service.model.core.InternalUmlService;
import com.huawang.business.service.model.core.property.UpdateModelFactory;
import com.huawang.core.definition.mof.UMLDefinitionConstants;
import com.huawang.core.definition.mof.instances.MetaClassInstance;
import com.huawang.core.semantics.entity.queryResult.MofResultContainer;
import com.huawang.core.semantics.entity.queryResult.QueryPropertyForm;
import com.huawang.mdesign.model.service.dto.CreateInstanceSpecificationDTO;
import com.huawang.mdesign.model.service.dto.InstacneSpecificationInfo;
import com.huawang.mdesign.model.service.dto.SlotInfo;
import org.apache.commons.collections4.CollectionUtils;

import java.util.*;
import java.util.stream.Collectors;
import java.util.stream.Stream;

public class InstanceTableSimulation {

    private final InternalUmlService internalUmlService;

    private final String projectId;

    private final CreateInstanceSpecificationDTO dto;
    private BatchCommand batchCommand = new BatchCommand();
    //更新结果
    private List<UpdateSlotValueDto> updateSlotValueDtoList = new ArrayList<>();
    private final Map<String, String> slotMapping = new HashMap<>();
    private final Map<String, String> absentIdsMapping = new HashMap<>();
    private Map<String, List<String>> instanceToClassiferMapping = new HashMap<>();
    private List<String> absentIds;
    private Map<String, MetaClassInstance> instanceMap;
    private List<MetaClassInstance> presentSlots;
    private List<String> presentIds;
    private MofResultContainer container;
    private String owner;

    public InstanceTableSimulation(String projectId,
                                   CreateInstanceSpecificationDTO dto,
                                   InternalUmlService internalUmlService) {
        this.internalUmlService = internalUmlService;
        this.projectId = projectId;
        this.dto = dto;
    }

    private void init() {
        List<String> ids = new ArrayList<>();
        Set<String> classifiers = new HashSet<>();
        Set<String> features = new HashSet<>();
        dto.getInstanceSpecifications().stream().forEach(instacneSpecificationInfo -> {
            ids.add(instacneSpecificationInfo.getId());
            instanceToClassiferMapping.put(instacneSpecificationInfo.getId(),
                    instacneSpecificationInfo.getClassifierIds());
            classifiers.addAll(instacneSpecificationInfo.getClassifierIds());
            features.addAll(instacneSpecificationInfo.getSlots().stream().map(SlotInfo::getFeatureId).collect(Collectors.toSet()));
        });

        instanceMap = internalUmlService.fetchInstances(Stream.concat(features.stream(), Stream.concat(ids.stream(),
                classifiers.stream())).collect(Collectors.toList()), projectId);
        absentIds = ids.stream().filter(it -> !instanceMap.containsKey(it)).collect(Collectors.toList());
        presentIds = new ArrayList<>(ids);
        presentIds.removeAll(absentIds);
        final List<MetaClassInstance> instances =
                ids.stream().map(instanceMap::get).filter(Objects::nonNull).collect(Collectors.toList());
        if (CollectionUtils.isEmpty(instances)) {
            return;
        }

        final QueryPropertyForm form =
                internalUmlService.getQueryPropertyForm(instances,
                        UMLDefinitionConstants.InstanceSpecification_slot);
        form.put(instances.get(0), UMLDefinitionConstants.Element_owner);
        container = internalUmlService.queryProperty(projectId, form);
        presentSlots = presentIds.stream().flatMap(id -> container.queryListInstanceProperty(id,
                        UMLDefinitionConstants.InstanceSpecification_slot).stream())
                .collect(Collectors.toList());
        final QueryPropertyForm form1 = internalUmlService.getQueryPropertyForm(presentSlots, UMLDefinitionConstants.Slot_definingFeature);
        container.addMofPropertyResult(internalUmlService.queryProperty(projectId, form1));
        owner = internalUmlService.queryInstanceProperty(projectId, instances.get(0),
                UMLDefinitionConstants.Element_owner).getId();
    }

    public void run() {
        //初始化数据
        init();
        //1 创建实例
        for (String absentId : absentIds) {
            final String newInstanceId = ModelConstantURI.initInstanceId();
            AddModelDTO addModelDTO = new AddModelDTO();
            addModelDTO.setType(CommandAddType.MODEL.getCode());
            addModelDTO.setParentId(owner);
            addModelDTO.setProjectId(projectId);
            addModelDTO.setMetaclass(UMLDefinitionConstants.InstanceSpecification);
            addModelDTO.setInstanceId(newInstanceId);
            batchCommand.appendAddModelDto(addModelDTO);
            batchCommand.appendUpdateModelDto(UpdateModelFactory.newInstance(projectId, newInstanceId,
                    UMLDefinitionConstants.InstanceSpecification_classifier,
                    instanceToClassiferMapping.get(absentId).stream().map(instanceMap::get).filter(Objects::nonNull).collect(Collectors.toList())));
            absentIdsMapping.put(absentId, newInstanceId);
        }

        //2 创建slot
        //已存在slot与featureId映射
        final Map<String, String> presentFeatureToSlotMapping = new HashMap<>();
        presentSlots.forEach(presentSlot -> {
            MetaClassInstance feature = container.queryInstanceProperty(presentSlot.getId(),
                    UMLDefinitionConstants.Slot_definingFeature);
            if (feature != null) {
                presentFeatureToSlotMapping.put(feature.getId(), presentSlot.getId());
            }
        });
        dto.getInstanceSpecifications().forEach(it -> {
            if (presentIds.contains(it.getId())) {
                for (SlotInfo slotInfo : it.getSlots()) {
                    if (presentFeatureToSlotMapping.containsKey(slotInfo.getFeatureId())) {
                        slotMapping.put(it.getId() + "_" + slotInfo.getFeatureId(),
                                presentFeatureToSlotMapping.get(slotInfo.getFeatureId()));
                    } else {
                        final String newSlotId = ModelConstantURI.initInstanceId();
                        AddModelDTO addModelDTO = new AddModelDTO();
                        addModelDTO.setType(CommandAddType.MODEL.getCode());
                        addModelDTO.setParentId(it.getId());
                        addModelDTO.setProjectId(projectId);
                        addModelDTO.setMetaclass(UMLDefinitionConstants.Slot);
                        addModelDTO.setInstanceId(newSlotId);
                        batchCommand.appendAddModelDto(addModelDTO);
                        batchCommand.appendUpdateModelDto(UpdateModelFactory.newInstance(projectId, newSlotId,
                                UMLDefinitionConstants.Slot_definingFeature,
                                instanceMap.get(slotInfo.getFeatureId())));
                        slotMapping.put(it.getId() + "_" + slotInfo.getFeatureId(),
                                newSlotId);
                    }
                }

            } else if (absentIdsMapping.containsKey(it.getId())) {
                for (SlotInfo slotInfo : it.getSlots()) {
                    final String newSlotId = ModelConstantURI.initInstanceId();
                    AddModelDTO addModelDTO = new AddModelDTO();
                    addModelDTO.setType(CommandAddType.MODEL.getCode());
                    addModelDTO.setParentId(absentIdsMapping.get(it.getId()));
                    addModelDTO.setProjectId(projectId);
                    addModelDTO.setMetaclass(UMLDefinitionConstants.Slot);
                    addModelDTO.setInstanceId(newSlotId);
                    batchCommand.appendAddModelDto(addModelDTO);
                    batchCommand.appendUpdateModelDto(UpdateModelFactory.newInstance(projectId, newSlotId,
                            UMLDefinitionConstants.Slot_definingFeature,
                            instanceMap.get(slotInfo.getFeatureId())));
                    slotMapping.put(absentIdsMapping.get(it.getId()) + "_" + slotInfo.getFeatureId(),
                            newSlotId);
                }
            }

        });

        //3 更新插槽
        createUpdateSlotValueDtoList();
    }

    private void createUpdateSlotValueDtoList() {
        for (InstacneSpecificationInfo instanceSpecification : dto.getInstanceSpecifications()) {
            for (SlotInfo slot : instanceSpecification.getSlots()) {
                UpdateSlotValueDto updateSlotValueDto = new UpdateSlotValueDto();
                updateSlotValueDto.setSlotId(slotMapping.get(absentIdsMapping.getOrDefault(instanceSpecification.getId(),
                        instanceSpecification.getId()) +
                        "_" + slot.getFeatureId()));
                final List<SlotValueInfo> newValues = slot.getValues().stream().map(it -> {
                    SlotValueInfo newValue = new SlotValueInfo();
                    newValue.setType(it.getType());
                    if (Objects.equals(it.getType(), UMLDefinitionConstants.InstanceValue)) {
                        if (it.getValue() != null) {
                            newValue.setValue(absentIdsMapping.get(it.getValue().toString()));
                        }
                    } else {
                        newValue.setValue(it.getValue());
                    }
                    return newValue;
                }).collect(Collectors.toList());
                updateSlotValueDto.setValues(newValues);
                updateSlotValueDtoList.add(updateSlotValueDto);
            }
        }

    }

    public BatchCommand getBatchCommand() {
        return batchCommand;
    }

    public List<UpdateSlotValueDto> getUpdateSlotValueDtoList() {
        return updateSlotValueDtoList;
    }
}
